package codename.carrot.bomberman.models;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.concurrent.CopyOnWriteArrayList;

import sheep.game.Sprite;
import android.graphics.Canvas;

/**
 *  Class representing a level
 * @author Group A4
 *
 */
public class Level extends Sprite {
	
	/**
	 *  List of all obstacles in the game
	 */
    private CopyOnWriteArrayList<AbstractObstacle> obstacles = new CopyOnWriteArrayList<AbstractObstacle>();
    /**
     * List of all the items in the game
     */
    private CopyOnWriteArrayList<CollectableItem> items = new CopyOnWriteArrayList<CollectableItem>();
    /**
     * List of all the players in the game
     */
    private CopyOnWriteArrayList<Player> players = new CopyOnWriteArrayList<Player>();
    /**
     * The local player
     */
    private Player myPlayer;
    private int width,
    height;
    
    /**
     * Constructor
     * @param obstacles the obstacles on this level
     * @param width the width (nr of tiles in x-direction)
     * @param height the height (nr of tiles in y-direction)
     */
    public Level(CopyOnWriteArrayList<AbstractObstacle> obstacles, int width, int height)  {
        this.width = width;
        this.height = height;
        this.obstacles = obstacles;                  
    }    
    
    /**
     *  Returns all current obstacles in this level
     * @return arrayList with obstacles
     */
    public CopyOnWriteArrayList<AbstractObstacle> getObstacles() {
        return obstacles;
    }
     
    /**
     *  Returns all current items in this level
     * @return arrayList with items
     */
    public CopyOnWriteArrayList<CollectableItem> getItems() {
        return items;
    }
    
    /**
     *  Adds an obstacle to this level
     * @param o
     */
    public void addObstacle(AbstractObstacle o) {        
        obstacles.add(o);
    }

    /**
     *  Removes an obstacle from this level
     * @param obstacle
     */
    public void removeObstacle(AbstractObstacle obstacle) {
        obstacles.remove(obstacle);
    }
    
    /**
     *  Adds an item to this level
     * @param item
     */
    public void addItem(CollectableItem item) {
        items.add(item);
    }

    /**
     *  Sets the local player reference
     * @param myPlayer
     */
    public void setMyPlayer(Player myPlayer) {
        this.myPlayer = myPlayer;
    }
    
    /**
     *  Returns all players
     * @return All players.
     */
    public CopyOnWriteArrayList<Player> getPlayers() {
        return players;
    }
    
    /**
     *  Sets the list of players
     * @param players
     */
    public void setPlayers(ArrayList<Player> players) {
        this.players.clear();
        if(players == null) return;
        for(Player p : players) {
            this.players.add(p);
        }
    }    
    
    /**
     *  Returns a collectable item if there exists one for given <code>xCoor</code> and <code>yCoor</code> coordinate
     * @param xCoor x-coordinate
     * @param yCoor y-Coordinate
     * @return A collectible item for the coordinate.
     */
    public CollectableItem getItem(int xCoor, int yCoor) {
        Iterator<CollectableItem> it = items.iterator();
        while(it.hasNext()) {
            CollectableItem i = it.next();
            if(i.getXcoorMap() == xCoor && i.getYcoorMap() == yCoor) {
                return i;
            }                                 
        }     
        return null;
    }
   
    /**
     * Removes an item from the level
     * @param item the item to remove
     */
    public void removeItem(CollectableItem item) {
        items.remove(item);
    }
        
    /**
     *  Returns an obstacle for given <code>xCoor</code> and <code>yCoor</code> coordinate
     * @param xCoor the x-coordinate
     * @param yCoor the y-coordinate
     * @return The obstacte at xCoor and yCoor.
     */
    public AbstractObstacle getObstacle(int xCoor, int yCoor) {
        Iterator<AbstractObstacle> it = obstacles.iterator();
        while(it.hasNext()) {
            AbstractObstacle o = it.next();
            if(o.getXcoorMap() == xCoor && o.getYcoorMap() == yCoor) {
                return o;
            }                                 
        }     
        return null;
    }                   
    
    /**
     * Updates the state of this level based on the number of miliseconds since last update
     */
    @Override
    public void update(float dt) {
        for(Player p : players) p.update(dt);
        
        Iterator<AbstractObstacle> itAo = obstacles.iterator();
        while(itAo.hasNext()) {
            itAo.next().update(dt);
        }
    
        Iterator<CollectableItem> itCi = items.iterator();
        while(itCi.hasNext()) {
            itCi.next().update(dt);
        }       
    }
    
    /**
     *  Draws the entire level, with players, items, boulders and other obstacles
     */
    @Override
    public void draw(Canvas c) {   
    	if(c == null) return;
        Iterator<CollectableItem> itCi = items.iterator();
        while(itCi.hasNext()) {            
            itCi.next().draw(c);
        }                
        
        Iterator<AbstractObstacle> itAo = obstacles.iterator();
        while(itAo.hasNext()) {
            itAo.next().draw(c);
        }
        for(Player p : players) {
            if(!p.isDead())
                p.draw(c);
        }
    }
    
    /**
     *  Returns the local player
     * @return myPlayer
     */
    public Player getMyPlayer() {
        return myPlayer;
    }

    /**
     *  Returns a specific Player object based on <code>playerId</code>
     * @param playerId the player to retrieve
     * @return Player
     */
    public Player getPlayer(int playerId) {
        for(Player p : players) {
            if(p.getPlayerId() == playerId) return p;
        }
        return null;
    }
    
    /**
     *  Finds and returns the index of a player
     * @param p the player
     * @return player index
     */
    private int getPlayerIndex(Player p) {
        for(int i=0; i < players.size(); i++) {
            if(players.get(i).getPlayerId() == p.getPlayerId()) {
                return i;
            }
        }
        return 0;
    }
    
    /**
     *  Returns the player alive before playerIndex
     * @param player Player
     * @return previous player alive
     */
    public Player getPreviousPlayerAlive(Player player) {
        int playerIndex = getPlayerIndex(player);
        if(playerIndex < 0) return null;
        if(playerIndex > players.size()-1) return null;
        for(int i=playerIndex-1; i >= 0; i--) {
            Player p = players.get(i);
            if(!p.isDead()) return p;
        }
        return null;                    
    }
    
    /**
     * Returns the last player alive
     * @return last player alive
     */
    public Player getLastPlayerAlive() {
        for(int i=players.size()-1; i <= 0; i--) {
            Player p = players.get(i);
            if(!p.isDead()) return p;
        }
        return null;
    }
    
    /**
     * Returns the next player alive (based on playerIndex)
     * @param player Player
     * @return Player
     */
    public Player getNextPlayerAlive(Player player) {
        int playerIndex = getPlayerIndex(player);
        if(playerIndex < 0) return null;
        if(playerIndex > players.size()-1) return null;
        for(int i=playerIndex+1; i < players.size(); i++) {
            Player p = players.get(i);
            if(!p.isDead()) return p;
        }
        return null;
    }
    
    /**
     *  Returns the first player alive
     * @return Player
     */
    public Player getFirstPlayerAlive() {
        for(int i=0; i < players.size(); i++) {
            Player p = players.get(i);
            if(!p.isDead()) return p;
        }
        return null;
    }
        
    /**
     *  Returns the (real) width of the level
     * @return width
     */
    public int getWidth() {
        return width;
    }
    
    /**
     *  Returns the (real) height of the level
     * @return height
     */
    public int getHeight() {
        return height;
    }
}
